home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Games / SprocketInvaders / Source / SpaceInvaders.c < prev    next >
Encoding:
Text File  |  2000-09-28  |  19.7 KB  |  926 lines  |  [TEXT/MPS ]

  1. //•    ------------------------------------------------------------------------------------------    •
  2. //•
  3. //•        Copyright ©1995, Chris De Salvo
  4. //•        Not to be distributed without expressed consent
  5. //•
  6. //•        Written by Chris De Salvo
  7. //•
  8. //•    ------------------------------------------------------------------------------------------    •
  9.  
  10. //•    ------------------------------    Includes
  11.  
  12. #include <Fonts.h>
  13. #include <stdio.h>
  14. #include <string.h>
  15.  
  16. #include "ErrorHandler.h"
  17. #include "EventHandler.h"
  18. #include "GameObject.h"
  19. #include "Graphics.h"
  20. #include "ObjectActions.h"
  21. #include "SIResources.h"
  22. #include "SoundHandler.h"
  23. #include "SpaceInvaders.h"
  24. #include "Sprite.h"
  25. #include "NetSprocketSupport.h"
  26. #include "CommonStuff.h"
  27.  
  28. //•    ------------------------------    Private Definitions
  29.  
  30. //#define RAND(x)                    ((Random() & 0x7FFF) % (x))
  31.  
  32. #define kPlayerVelocity                2L    //•    Speed at which player moves
  33. #define kPlayerShotVelocity            6L    //•    Speed at which player shots travel
  34. #define kPointsVelocity                -2L    //•    Vertical speed of the "points" object (negative is up)
  35. #define kEnemyVelocity                5L    //•    Speed at which enemies move
  36. #define kEnemyShotVelocity            4L    //•    Speed at which enemy shots drop
  37.  
  38. #define kEnemyOffset                8    //•    Space between columns of enemies
  39.  
  40. //•    ------------------------------    Private Types
  41.  
  42. enum
  43. {
  44.     scPlayer,
  45.     scPlayer2,
  46.     scPlayerShot,
  47.     scPoints,
  48.     scEnemy,
  49.     scEnemyShot,
  50.     scNumSprites
  51. };
  52.  
  53. //•    ------------------------------    Private Variables
  54.  
  55. static SpritePtr    gSpriteCache[scNumSprites];    //•    Sprite artwork is cached here so that multiple items using
  56.                                                 //•    the same artwork only need one copy.
  57. static long                gScore;
  58. static short            gWave;
  59. static short            gEnemyGas;
  60. static int                gEnemyNewDirection;
  61. static unsigned long    gFrameRateBaseTime;        //•    Base time for this game to count frame rate
  62.  
  63. float gGreenAccum;
  64. float gRedAccum;
  65.  
  66. extern int gMyRand;
  67. extern int gHisRand;
  68. extern int gMyCallsToRand;
  69. extern int gHisCallsToRand;
  70.  
  71. //•    ------------------------------    Private Functions
  72.  
  73. static void AddPlayers(void);
  74. static void AddEnemies(void);
  75. static void CollideShotsToEnemies(void);
  76. static void AddPoints(short x, short y);
  77. static void DisplayGameOver(CGrafPtr backBuff);
  78. static void DropEnemies(void);
  79. static void CollideShotsToPlayers(void);
  80. static void AdvanceEnemies(void);
  81. static void DisplayFrameRate(void);
  82. static void EndGame(CGrafPtr backBuff);
  83. static void GetGreenInput(void);
  84. static void GetRedInput(void);
  85.  
  86. //•    ------------------------------    Public Variables
  87.  
  88. Boolean    gGameInProgress = false;
  89. Boolean    gTwoPlayers = false;
  90.  
  91. GameObjectPtr    gEnemyList = nil;
  92. GameObjectPtr    gEnemyShotList = nil;
  93. GameObjectPtr    gPlayerList = nil;
  94. GameObjectPtr    gGreenPlayerShotList = nil;
  95. GameObjectPtr     gRedPlayerShotList = nil;
  96. GameObjectPtr    gMiscObjectList = nil;
  97.  
  98. int        gEnemyTask;
  99. int        gEnemiesChangeDirection;
  100. short    gEnemyVelocity;
  101. short    gNumEnemies;
  102.  
  103. unsigned long    gEnemyLevel;
  104. unsigned long    gNumEnemiesProcessed;
  105.  
  106. int            gNumGreenPlayerLives = 0;
  107. int            gNumRedPlayerLives = 0;
  108.  
  109. Boolean            gNetPlay;
  110. extern Boolean    gReceivedInput;
  111.  
  112. //•    --------------------    InitNewGame
  113.  
  114. static void InitNewRound(short wave)
  115. {
  116.     GameObjectDisposeList(&gEnemyList);
  117.     GameObjectDisposeList(&gEnemyShotList);
  118.     GameObjectDisposeList(&gPlayerList);
  119.     GameObjectDisposeList(&gGreenPlayerShotList);
  120.     GameObjectDisposeList(&gRedPlayerShotList);
  121.     GameObjectDisposeList(&gMiscObjectList);
  122.  
  123.     AddPlayers();
  124.     AddEnemies();
  125.     
  126.     gEnemyTask = kEnemyMovingRight;
  127.     gEnemiesChangeDirection = 0;
  128.     gEnemyVelocity = kEnemyVelocity;
  129.     gEnemyLevel = 1;
  130.     gWave = wave;
  131. }
  132.  
  133. static void NextRound(void)
  134. {
  135.     InitNewRound(gWave + 1);
  136. }
  137.  
  138.  
  139. void
  140. InitNewGame(short wave)
  141. {
  142.     int i;
  143.     
  144.     for (i = 0; i < numInputs; i++)
  145.         ISpElement_Flush(gInputElements[i]);
  146.     
  147.     gGreenAccum = 0;
  148.     gRedAccum = 0;
  149.  
  150.     gGameInProgress = true;
  151.     gScore = 0L;
  152.  
  153.     gFrameRateBaseTime = 0L;
  154.  
  155.     for (i = 0; i < scNumSprites; i++)
  156.         SpriteDispose(&(gSpriteCache[i]));
  157.  
  158.     gSpriteCache[scPlayer] = SpriteLoad(kSPRTPlayer);
  159.     if (! gSpriteCache[scPlayer])
  160.         FatalError("Could not load player tank.");
  161.  
  162.     gSpriteCache[scPlayer2] = SpriteLoad(kSPRTPlayer2);
  163.     if (! gSpriteCache[scPlayer])
  164.         FatalError("Could not load player 2 tank.");
  165.  
  166.     gSpriteCache[scPlayerShot] = SpriteLoad(kSPRTPlayerShot);
  167.     if (! gSpriteCache[scPlayerShot])
  168.         FatalError("Could not load player shot.");
  169.         
  170.     gSpriteCache[scPoints] = SpriteLoad(kSPRTPoints);
  171.     if (! gSpriteCache[scPoints])
  172.         FatalError("Could no load points.");
  173.  
  174.     gSpriteCache[scEnemy] = SpriteLoad(kSPRTEnemy);
  175.     if (! gSpriteCache[scEnemy])
  176.         FatalError("Could no load enemy.");
  177.  
  178.     gSpriteCache[scEnemyShot] = SpriteLoad(kSPRTEnemyShot);
  179.     if (! gSpriteCache[scEnemyShot])
  180.         FatalError("Could no load enemy shot.");
  181.  
  182.  
  183.     gNumGreenPlayerLives = 3;
  184.     
  185.     if (gTwoPlayers)
  186.         gNumRedPlayerLives = 3;
  187.     else
  188.         gNumRedPlayerLives = 0;
  189.     
  190.     GraphicsActive();
  191.  
  192.     ISpResume();
  193.         
  194.     InitNewRound(wave);
  195. }
  196.  
  197. //•    --------------------    GameLoop
  198.  
  199. void
  200. GameLoop()
  201. {
  202. CGrafPtr            backBuff;
  203.     ISpElementEvent    event;
  204.     Boolean            wasEvent;
  205.         
  206.     if (gNetPlay)
  207.     {
  208.         if (gIAmHost)
  209.         {
  210.             GetGreenInput();
  211.             SendInputState(gGameKeys.greenLeft, gGameKeys.greenRight, gGameKeys.greenFire);
  212.             GetRedInput();
  213.         }
  214.         else
  215.         {
  216.             GetRedInput();
  217.             SendInputState(gGameKeys.redLeft, gGameKeys.redRight, gGameKeys.redFire);
  218.             GetGreenInput();
  219.         }    
  220.     }
  221.     else
  222.     {
  223.         GetGreenInput();
  224.         if (gTwoPlayers)
  225.             GetRedInput();
  226.     }
  227.     
  228.  
  229.     //•    Perform player/missile collission detection
  230.     CollideShotsToEnemies();
  231.     CollideShotsToPlayers();
  232.  
  233.     //•    If any enemy hit a boundry on the last run, switch directions
  234.     if (gEnemiesChangeDirection)
  235.     {
  236.         DropEnemies();
  237.         gEnemiesChangeDirection = 0;
  238.         gEnemyTask = kEnemyDropping;
  239.     }
  240.  
  241.     gEnemyGas = gWave + 1;
  242.  
  243.     //•    Get a reference to the back buffer
  244.     DSpContext_GetBackBuffer(gDisplayContext, kDSpBufferKind_Normal, &backBuff);
  245.  
  246.  
  247.     //•    Check the abort key
  248.     ISpElement_GetNextEvent(gInputElements[abort], sizeof (event), &event, &wasEvent);
  249.     if (gGotEndGameMessage || (wasEvent && (event.data == kISpButtonDown)))
  250.     {
  251.         if (gNetPlay && !gGotEndGameMessage)
  252.             SendEndGame();
  253.         EndGame(backBuff);
  254.         return;
  255.     }
  256.  
  257.     //•    Have all game objects perform their actions
  258.     AdvanceEnemies();
  259.     GameObjectListAdvance(gPlayerList);
  260.     GameObjectListAdvance(gEnemyShotList);
  261.     GameObjectListAdvance(gGreenPlayerShotList);
  262.     GameObjectListAdvance(gRedPlayerShotList);
  263.     GameObjectListAdvance(gMiscObjectList);
  264.  
  265.     //•    Draw all game objects
  266.     GameObjectListDraw(gEnemyList, backBuff);
  267.     GameObjectListDraw(gPlayerList, backBuff);
  268.     GameObjectListDraw(gEnemyShotList, backBuff);
  269.     GameObjectListDraw(gGreenPlayerShotList, backBuff);
  270.     GameObjectListDraw(gRedPlayerShotList, backBuff);
  271.     GameObjectListDraw(gMiscObjectList, backBuff);
  272.  
  273.     //•    Check for end of wave/game
  274.     if ((gNumGreenPlayerLives < 1) && (gNumRedPlayerLives < 1))
  275.     {
  276.         EndGame(backBuff);
  277.         return;
  278.     }
  279.     
  280.     if (gNumEnemies < 1)
  281.     {
  282.         NextRound();
  283.     }
  284.  
  285.  
  286.     //•    Update the frame rate display information
  287.     DisplayFrameRate();
  288.  
  289.     //•    Redraw the entire screen.  This is done last so that if the game over screen was drawn it gets blit for free
  290.     GraphicsUpdateScreen();
  291.  
  292. }
  293.  
  294. static Boolean GetAutoFireButton(ISpElementReference inElement)
  295. {
  296.     OSStatus error;
  297.     UInt32 input;
  298.     Boolean wasEvent;
  299.     Boolean fire = false;
  300.     
  301.     // poll    
  302.     error = ISpElement_GetSimpleState(inElement, &input);
  303.     if (!error && (input == kISpButtonDown)) 
  304.         { fire = true; } 
  305.  
  306.     // but don't miss fast clicks or macros
  307.     do
  308.     {
  309.         ISpElementEvent event;
  310.  
  311.         error = ISpElement_GetNextEvent(inElement, sizeof(ISpElementEvent), &event, &wasEvent);
  312.  
  313.         if (!error && wasEvent && (event.data == kISpButtonDown))
  314.         {
  315.             fire = true; 
  316.             break;
  317.         }
  318.     } while(wasEvent && !error);
  319.     
  320.     // flush the queue
  321.     ISpElement_Flush(inElement);
  322.     
  323.     return fire;
  324. }
  325.  
  326. void GetGreenInput(void)
  327. {
  328.     UInt32            input;
  329.     
  330.     if (!gNetPlay || (gNetPlay && gIAmHost))    // in netplay, the host is the green player, and the joiner the red
  331.     {
  332.         //•    Check the movement axis
  333.         ISpElement_GetSimpleState(gInputElements[greenMovement], &input);
  334.  
  335.         gGameKeys.greenLeft = false;
  336.         gGameKeys.greenRight = false;
  337.         
  338.         if (input < 0x2FFFFFFF)
  339.             { gGameKeys.greenLeft = true; }
  340.         else if (input > 0xBFFFFFFF)
  341.             { gGameKeys.greenRight =  true; }
  342.         else if (input < 0x5FFFFFFF)
  343.         { 
  344.             gGreenAccum += ((float) input - (float) 0x5FFFFFFF) / (float) 0x3FFFFFFF;
  345.              if (gGreenAccum < -1) { gGameKeys.greenLeft = true; gGreenAccum += 1; }
  346.          }
  347.          else if (input > 0x9FFFFFFF)
  348.          {
  349.             gGreenAccum += ((float) input - (float) 0x9FFFFFFF) / (float) 0x3FFFFFFF;
  350.              if (gGreenAccum > 1) { gGameKeys.greenRight = true; gGreenAccum -= 1; }
  351.          }
  352.         
  353.         //•    Check the fire button
  354.         gGameKeys.greenFire = GetAutoFireButton(gInputElements[greenFire]);
  355.     }
  356.     else if (gNetPlay && !gIAmHost)    //wait for player 1's input
  357.     {
  358.         gReceivedInput = false;
  359.         while (!gReceivedInput && !gGotEndGameMessage)
  360.             HandleNetworking();
  361.     }
  362. }
  363.  
  364. void GetRedInput(void)
  365. {
  366.     UInt32            input;
  367.     if (!gNetPlay || (gNetPlay && !gIAmHost))    // in netplay, the host is the green player, and the joiner the red
  368.     {
  369.         //•    Check the movement axis
  370.         ISpElement_GetSimpleState(gInputElements[redMovement], &input);
  371.  
  372.         gGameKeys.redLeft = false;
  373.         gGameKeys.redRight = false;
  374.         
  375.         if (input < 0x3FFFFFFF)
  376.             { gGameKeys.redLeft = true; }
  377.         else if (input > 0xAFFFFFFF)
  378.             { gGameKeys.redRight =  true; }
  379.         else if (input < 0x5FFFFFFF)
  380.         { 
  381.             gRedAccum += ((float) input - (float) 0x5FFFFFFF) / (float) 0x2FFFFFFF;
  382.              if (gRedAccum < -1) { gGameKeys.redLeft = true; gRedAccum += 1; }
  383.          }
  384.          else if (input > 0x9FFFFFFF)
  385.          {
  386.             gRedAccum += ((float) input - (float) 0x9FFFFFFF) / (float) 0x2FFFFFFF;
  387.              if (gRedAccum > 1) { gGameKeys.redRight = true; gRedAccum -= 1; }
  388.          }
  389.  
  390.         //•    Check the fire button
  391.         gGameKeys.redFire = GetAutoFireButton(gInputElements[redFire]);
  392.     }
  393.     else if (gNetPlay && gIAmHost)    //wait for player 2's input
  394.     {
  395.         gReceivedInput = false;
  396.         while (!gReceivedInput && !gGotEndGameMessage)
  397.             HandleNetworking();
  398.     }
  399. }
  400.  
  401. //•    --------------------    AddPlayers
  402.  
  403. static void
  404. AddPlayers(void)
  405. {
  406. Rect            r;
  407. GameObjectPtr    go;
  408.  
  409.     go = GameObjectAllocate();
  410.     if (! go)
  411.         FatalError("Could not allocate player object.");
  412.         
  413.     GameObjectAddToList(&gPlayerList, go);
  414.     
  415.     SetRect(&r, 20, 0, 620, 0);
  416.     GameObjectSetBounds(go, &r);
  417.     GameObjectSetSprite(go, gSpriteCache[scPlayer]);
  418.     
  419.     go->kind = objectGreenPlayer;
  420.     go->screenX = 20;
  421.     go->screenY = 412;
  422.     go->velocityH = kPlayerVelocity;
  423.     go->velocityV = 0;
  424.     go->action = GreenPlayerAction;
  425.  
  426.     //•    Sloppy way to do this, but hey, I'm in a hurry
  427.     if (gTwoPlayers)
  428.     {
  429.         go = GameObjectAllocate();
  430.         if (! go)
  431.             FatalError("Could not allocate player 2 object.");
  432.             
  433.         GameObjectAddToList(&gPlayerList, go);
  434.         
  435.         SetRect(&r, 20, 0, 620, 0);
  436.         GameObjectSetBounds(go, &r);
  437.         GameObjectSetSprite(go, gSpriteCache[scPlayer2]);
  438.         
  439.         go->kind = objectRedPlayer;
  440.         go->screenX = 640 - 20;
  441.         go->screenY = 412;
  442.         go->velocityH = kPlayerVelocity;
  443.         go->velocityV = 0;
  444.         go->action = RedPlayerAction;
  445.     }
  446. }
  447.  
  448. //•    --------------------    AddEnemies
  449.  
  450. static void
  451. AddEnemies(void)
  452. {
  453. Rect            r;
  454. GameObjectPtr    go;
  455. int            i, j;
  456. int            w, h;
  457. int            x, x2, y;
  458.  
  459.     gNumEnemies = 0;
  460.  
  461.     //•    Find dimensions of enemy sprite so we can center the rows to begin with
  462.     w = SpriteWidth(gSpriteCache[scEnemy], 0);
  463.     w += kEnemyOffset;
  464.     h = SpriteHeight(gSpriteCache[scEnemy], 0);
  465.     h += kEnemyOffset;
  466.     
  467.     x2 = w * kNumEnemyColumns;
  468.     x2 = 320 - (x2 / 2);
  469.  
  470.     y = 100;
  471.  
  472.     SetRect(&r, 20, 20, 620, 400);
  473.  
  474.     for (i = 0; i < kNumEnemyRows; i++)
  475.     {
  476.         x = x2;
  477.         
  478.         for (j = 0; j < kNumEnemyColumns; j++)
  479.         {
  480.             go = GameObjectAllocate();
  481.             if (! go)
  482.                 FatalError("Could not allocate enemy object.");
  483.             
  484.             GameObjectAddToList(&gEnemyList, go);
  485.             
  486.             GameObjectSetBounds(go, &r);
  487.             GameObjectSetSprite(go, gSpriteCache[scEnemy]);
  488.             
  489.             go->kind = objectEnemy;
  490.             go->screenX = x;
  491.             go->screenY = y;
  492.             go->velocityH = kEnemyVelocity;
  493.             go->velocityV = 0;
  494.             go->action = EnemyAction;
  495.             
  496.             x += w;
  497.             gNumEnemies++;
  498.         }
  499.         
  500.         y += h;
  501.     }
  502. }
  503.  
  504. //•    --------------------    PlayerShoot
  505.  
  506. void
  507. PlayerShoot(GameObjectPtr whichPlayer)
  508. {
  509. Rect            r;
  510. GameObjectPtr    go;
  511. GameObjectPtr    shotList;
  512.  
  513.  
  514.     if (whichPlayer->kind == objectGreenPlayer)
  515.     {
  516.         if (gGreenPlayerShotList)
  517.             return;
  518.         else
  519.             shotList = gGreenPlayerShotList;
  520.     }
  521.     else
  522.     {
  523.         if (gRedPlayerShotList)
  524.             return;
  525.         else
  526.             shotList = gRedPlayerShotList;
  527.     }
  528.  
  529.     SoundHandlerPlay(soundPlayerFire, whichPlayer->screenX, whichPlayer->screenY);
  530.  
  531.     go = GameObjectAllocate();
  532.     if (! go)
  533.         FatalError("Could not allocate player shot.");
  534.         
  535.     GameObjectAddToList(&shotList, go);
  536.     
  537.     SetRect(&r, 20, 0, 620, 0);
  538.     GameObjectSetBounds(go, &r);
  539.     GameObjectSetSprite(go, gSpriteCache[scPlayerShot]);
  540.     
  541.     go->screenX = whichPlayer->screenX;
  542.     go->screenY = whichPlayer->screenY - SpriteHeight(whichPlayer->sprite, whichPlayer->frame) - 1;
  543.     go->velocityH = 0;
  544.     go->velocityV = kPlayerShotVelocity;
  545.     go->action = PlayerShotAction;
  546.     
  547.     if (whichPlayer->kind == objectRedPlayer)
  548.     {
  549.         go->kind = objectRedPlayerShot;
  550.         if (! gRedPlayerShotList)
  551.             gRedPlayerShotList = shotList;
  552.     }
  553.     else
  554.     {
  555.         go->kind = objectGreenPlayerShot;
  556.         if (! gGreenPlayerShotList)
  557.             gGreenPlayerShotList = shotList;
  558.     }
  559. }
  560.  
  561. //•    --------------------    CollideShotsToEnemies
  562.  
  563. static void
  564. CollideShotsToEnemies()
  565. {
  566. GameObjectPtr    shot;
  567. GameObjectPtr    target;
  568.  
  569.     if (! gEnemyList)
  570.         return;
  571.         
  572.     //•    Iterate over the green player shots
  573.     if (gGreenPlayerShotList)
  574.     {
  575.         for (shot = gGreenPlayerShotList; shot; shot = shot->next)
  576.         {
  577.             //•    Iterate over the enemies
  578.             for (target = gEnemyList; target; target = target->next)
  579.             {
  580.             Rect    u;    //•    Union of shot and enemy screen rects
  581.             
  582.                 SectRect(&shot->screenRect, &target->screenRect, &u);
  583.                 if (! EmptyRect(&u))
  584.                 {
  585.                     SoundHandlerPlay(soundEnemyHit, target->screenX, target->screenY);
  586.                     
  587.                     shot->action = PlayerShotDestroy;
  588.                     target->action = EnemyDestroy;
  589.                     AddPoints(target->screenX, target->screenY);
  590.                 }
  591.             }
  592.         }
  593.     }
  594.     
  595.     //•    Iterate over the red player shots
  596.     if (gRedPlayerShotList)
  597.     {
  598.         for (shot = gRedPlayerShotList; shot; shot = shot->next)
  599.         {
  600.             //•    Iterate over the enemies
  601.             for (target = gEnemyList; target; target = target->next)
  602.             {
  603.             Rect    u;    //•    Union of shot and enemy screen rects
  604.             
  605.                 SectRect(&shot->screenRect, &target->screenRect, &u);
  606.                 if (! EmptyRect(&u))
  607.                 {
  608.                     SoundHandlerPlay(soundEnemyHit, target->screenX, target->screenY);
  609.                     
  610.                     shot->action = PlayerShotDestroy;
  611.                     target->action = EnemyDestroy;
  612.                     AddPoints(target->screenX, target->screenY);
  613.                 }
  614.             }
  615.         }
  616.     }
  617. }
  618.  
  619. //•    --------------------    CollideShotsToPlayers
  620.  
  621. static void
  622. CollideShotsToPlayers(void)
  623. {
  624. GameObjectPtr    shot;
  625. GameObjectPtr    target;
  626. GameObjectPtr    next;
  627.  
  628.     if (! gEnemyShotList || ! gPlayerList)
  629.         return;
  630.  
  631.     target = gPlayerList;
  632.     next = target->next;
  633.     while (target != nil)
  634.     {
  635.         if (target->refCon > 0)
  636.             return;
  637.  
  638.         //•    Iterate over the enemy shots
  639.         for (shot = gEnemyShotList; shot; shot = shot->next)
  640.         {
  641.         Rect    u;    //•    Union of shot and enemy screen rects
  642.             
  643.             SectRect(&shot->screenRect, &target->screenRect, &u);
  644.             if (! EmptyRect(&u))
  645.             {
  646.                 SoundHandlerPlay(soundPlayerHit,target->screenX, target->screenY);
  647.  
  648.                 shot->action = EnemyShotDestroy;
  649.                 target->action = PlayerDestroy;
  650.                 
  651.                 if (target->kind == objectGreenPlayer)
  652.                 {
  653.                     gNumGreenPlayerLives--;
  654.                     if (gNumGreenPlayerLives < 1)
  655.                         GameObjectRemoveFromList(&gPlayerList, target);
  656.                 }
  657.                 else
  658.                 {
  659.                     gNumRedPlayerLives--;
  660.                     if (gNumRedPlayerLives < 1)
  661.                         GameObjectRemoveFromList(&gPlayerList, target);
  662.                 }
  663.             }
  664.         }
  665.         
  666.         target = next;
  667.         next = target->next;
  668.     }
  669. }
  670.  
  671. //•    --------------------    AddPoints
  672.  
  673. static void
  674. AddPoints(short x, short y)
  675. {
  676. Rect            r;
  677. GameObjectPtr    go;
  678.  
  679.     gScore += 100L;
  680.  
  681.     go = GameObjectAllocate();
  682.     if (! go)
  683.         FatalError("Could not allocate player shot.");
  684.         
  685.     GameObjectAddToList(&gMiscObjectList, go);
  686.     
  687.     SetRect(&r, 0, 0, 640, 440);
  688.     GameObjectSetBounds(go, &r);
  689.     GameObjectSetSprite(go, gSpriteCache[scPoints]);
  690.     
  691.     go->screenX = x;
  692.     go->screenY = y;
  693.     go->velocityH = 0;
  694.     go->velocityV = kPointsVelocity;
  695.     go->action = PointsAction;
  696. }
  697.  
  698. //•    --------------------    DisplayGameOver
  699.  
  700. static void
  701. DisplayGameOver(CGrafPtr backBuff)
  702. {
  703. GrafPtr    oldPort;
  704. Str255    str = "\pGame Over!";
  705. short    width;
  706. short    offset;
  707.  
  708.     GetPort(&oldPort);
  709.     SetPort((GrafPtr) backBuff);
  710.     
  711.     TextFont(geneva);
  712.     TextFace(bold);
  713.     TextSize(48);
  714.     
  715.     //•    Find the width of our string
  716.     width = StringWidth(str);
  717.  
  718.     //•    Find an offset that will center the string in the buffer
  719.     offset = backBuff->portRect.right - backBuff->portRect.left;
  720.     offset /= 2;
  721.     offset -= width / 2;
  722.     
  723.     MoveTo(offset, 200);
  724.     RGBForeColor(&rgbYellow);
  725.     DrawString(str);
  726.  
  727.     MoveTo(offset - 2, 200);
  728.     RGBForeColor(&rgbBlue);
  729.     DrawString(str);
  730.  
  731.     MoveTo(offset - 4, 200);
  732.     RGBForeColor(&rgbRed);
  733.     DrawString(str);
  734.  
  735.     RGBForeColor(&rgbBlack);
  736.     
  737.     SetPort(oldPort);
  738.     GraphicsSetRectDirty(&backBuff->portRect);
  739. }
  740.  
  741. //•    --------------------    DropEnemies
  742.  
  743. static void
  744. DropEnemies()
  745. {
  746. GameObjectPtr    current, next;
  747.  
  748.     gEnemyNewDirection = gEnemiesChangeDirection;
  749.  
  750.     do
  751.     {
  752.         current = gEnemyList;
  753.         gNumEnemiesProcessed = 0;
  754.         
  755.         do
  756.         {
  757.             next = current->next;
  758.             current->action(current);
  759.             current = next;
  760.         } while (next);
  761.     } while (gNumEnemiesProcessed);
  762.  
  763.     gEnemyLevel++;
  764. }
  765.  
  766. //•    --------------------    EnemyShoot
  767.  
  768. void
  769. EnemyShoot(GameObjectPtr whichEnemy)
  770. {
  771. Rect            r;
  772. GameObjectPtr    go;
  773.  
  774.     go = GameObjectAllocate();
  775.     if (! go)
  776.         FatalError("Could not allocate enemy shot.");
  777.         
  778.     SoundHandlerPlay(soundEnemyFire, whichEnemy->screenX, whichEnemy->screenY);
  779.  
  780.     GameObjectAddToList(&gEnemyShotList, go);
  781.     
  782.     SetRect(&r, 20, 0, 620, 412);
  783.     GameObjectSetBounds(go, &r);
  784.     GameObjectSetSprite(go, gSpriteCache[scEnemyShot]);
  785.     
  786.     go->screenX = whichEnemy->screenX;
  787.     go->screenY = whichEnemy->screenY + 1;
  788.     go->velocityH = 0;
  789.     go->velocityV = kEnemyShotVelocity;
  790.     go->action = EnemyShotAction;
  791. }
  792.  
  793. //•    --------------------    AdvanceEnemies
  794.  
  795. static void
  796. AdvanceEnemies()
  797. {
  798. GameObjectPtr    current, next;
  799. unsigned long    oldProcessed;
  800.  
  801.     do
  802.     {
  803.         gNumEnemiesProcessed = 0;
  804.         current = gEnemyList;
  805.         
  806.         do
  807.         {
  808.             oldProcessed = gNumEnemiesProcessed;
  809.             next = current->next;
  810.             current->action(current);
  811.             
  812.             //•    If we moved an alien, use up some alien gas
  813.             if (oldProcessed != gNumEnemiesProcessed)
  814.                 gEnemyGas--;
  815.                 
  816.             current = next;
  817.         } while (next && gEnemyGas);
  818.         
  819.         //•    If no enemies were processed then they're all at the same level, so move on
  820.         if (gNumEnemiesProcessed == 0)
  821.         {
  822.             gEnemyLevel++;
  823.             
  824.             if (gEnemyTask == kEnemyDropping)
  825.                 gEnemyTask = gEnemyNewDirection;
  826.         }
  827.  
  828.     } while (gEnemyGas && (gNumEnemies > 0));
  829. }
  830.  
  831. //•    --------------------    DisplayFrameRate
  832.  
  833. static void
  834. DisplayFrameRate(void)
  835. {
  836. static unsigned long    lastTime = 0;
  837. static unsigned long    frames = 0;
  838. unsigned long        elapsedTime;
  839. Str255            str;
  840. GrafPtr            oldPort;
  841. CGrafPtr            underlay;
  842. GDHandle            device, oldDevice;
  843. Rect                r;
  844.  
  845.     //•    Initialize the stats at the beginning of each game
  846.     //•    gFrameRateBaseTime is reset in InitNewGame()
  847.     if (gFrameRateBaseTime == 0)
  848.     {
  849.         gFrameRateBaseTime = TickCount();
  850.         lastTime = 0L;
  851.         frames = 0L;
  852.     }
  853.  
  854.     //•    Bump the frame counter
  855.     frames++;
  856.     
  857.     //•    Find the total elapsed time and convert it to seconds
  858.     elapsedTime = TickCount() - gFrameRateBaseTime;
  859.  
  860.     if ( ( elapsedTime - lastTime ) > 120 )
  861.     {
  862.         lastTime = elapsedTime;
  863.         elapsedTime /= 60;
  864.         sprintf((char *) str + 1, "FPS:   %0.4ld", frames / elapsedTime);
  865.         str[0] = strlen ((char *) str + 1);
  866.  
  867.         GraphicsGetUnderlayGrafPort(&underlay, &device);
  868.  
  869.         GetPort(&oldPort);
  870.         oldDevice = GetGDevice();
  871.         
  872.         SetPort((GrafPtr) underlay);
  873.         SetGDevice(device);
  874.         
  875.         RGBForeColor(&rgbBlack);
  876.         SetRect(&r, 5, 420, 90, 435);
  877.         PaintRect(&r);
  878.  
  879.         MoveTo(5, 435);
  880.         TextFont(systemFont);
  881.         TextFace(bold);
  882.         TextSize(12);
  883.         
  884.         RGBForeColor(&rgbYellow);
  885.         DrawString(str);
  886.         RGBForeColor(&rgbBlack);
  887.         
  888.         SetPort(oldPort);
  889.         SetGDevice(oldDevice);
  890.         
  891.         GraphicsSetUnderlayRectDirty(&r);
  892.     }
  893. }
  894.  
  895. //•    --------------------    EndGame
  896.  
  897. static void
  898. EndGame(CGrafPtr backBuff)
  899. {
  900.     Boolean OKHit;
  901.     
  902.     ISpSuspend();
  903.     DisplayGameOver(backBuff);
  904.     GraphicsUpdateScreen();
  905.     GraphicsPaused();
  906.     ShowCursor();
  907.     gGameInProgress = false;
  908.     if (gNetGame)
  909.     {
  910.         if (gIAmHost)
  911.             gNetState = kHosting;
  912.         else
  913.             gNetState = kJoining;
  914.         OKHit = WaitForAllPlayers();
  915.         if (OKHit == false)
  916.             ShutdownNetworking();
  917.         else
  918.         {
  919.             gNetState = kStarting;
  920.             SendStartGame();
  921.             InitNewGame(1);
  922.         }
  923.     }    
  924.     
  925. }
  926.